Profiler05-Session 字段定义
先说明一下字段的定义,和 Session 比较简单的零碎边缘,把内部的具体实现留到下次说明
Config
Session 的初始化配置表
C++
struct ProfilerConfig
{
// 初始化后是否立刻启用运行时采集。
//
// 注意:
// 这是 runtime enabled,不是编译期 CHIKA_ENABLE_PROFILING
bool enabled = true;
// History 最多保存多少帧 capture
size_t historyCapacity = 300;
// 每个线程默认 event buffer 容量。
size_t threadBufferCapacity = 65'536;
// hitch 阈值,单位 ns。
// 33,333,333ns大约是 33.33ms。
uint64_t hitchThresholdNs = 33'333'333;
};Session 字段说明
可以看到有内部成员 ——
C++
ProfilerThreadRegistry m_threads;
ProfilerAggregator m_aggregator;
ProfilerHistory m_history;
std::atomic<bool> m_initialized{ false };
std::atomic<bool> m_enabled{ false };
std::atomic<uint64_t> m_currentFrameId{ kInvalidProfilerFrameId };
uint64_t m_frameStartNs = 0;
uint64_t m_hitchThresholdNs = 33'333'333;
std::mutex m_frameMutex;
std::mutex m_gpuMutex;
std::unordered_map<uint64_t, std::vector<ProfilerGpuZone>> m_pendingGpu;这些字段可以先粗分成四类:
子系统对象
m_threadsm_aggregatorm_historym_pendingGpu
运行时状态位
m_initializedm_enabled
当前帧状态
m_currentFrameIdm_frameStartNsm_hitchThresholdNs
同步保护
m_frameMutexm_gpuMutex
子系统
m_threads:线程注册与线程事件 buffer 的入口,具体放到 ThreadRegistry 模块m_aggregator:帧尾聚合器,具体放到 Aggregator 模块m_history:保存已经完成的 frame capture,具体放到 History 模块m_pendingGpu:暂存尚未挂回 CPU frame 的 GPU timing
状态位
std::atomic<bool> m_initialized{ false };- 记录 Session 是否已经初始化
std::atomic<bool> m_enabled{ false };- 记录 Session 当前是否允许运行时采集事件
当前帧状态
使用 std::atomic<uint64_t> m_currentFrameId{ kInvalidProfilerFrameId }; 进行记录当前帧的 ID
- m_currentFrameId == invalid: 当前没有打开 frame
- m_currentFrameId == 某个 frameId: 当前处于某一帧采集范围内
使用
m_frameStartNs记录当前帧开始的时间m_hitchThresholdNs判断一帧是否 hitch 的阈值
锁
使用 std::mutex m_frameMutex; 维护 frame 的相关状态,主要保护 BeginFrame / EndFrame 这一类 frame 生命周期操作中的状态一致性 使用 std::mutex m_gpuMutex; 维护 GPU pending 的相关状态
具体维护了什么状态在后期说明,同时值得注意的,此处使用std::scoped_lock 进行一次锁多个 mutex
这里它用来保证初始化 / 关闭时同时重置 frame 状态和 GPU pending 状态,不让两个状态被并发看到不一致,以初始化作为例子 ——
假设线程A在执行初始化方法,与此同时另一个线程B在提交 GPU Timing,而 SubmitGpuTimings 会在持有 m_gpuMutex 的情况下执行 GPU timing 关联逻辑。它会查询 / 替换 m_history,也可能写入 m_pendingGpu
假设初始化方法不锁m_gpuMutex的话,可能发生 ——
Thread A: Initialize()
m_history.Clear()
Thread B: SubmitGpuTimings()
发现 history 里没有这个 frame
把 GPU timing 放进 m_pendingGpu
Thread A: Initialize()
m_pendingGpu.clear()导致 Thread B 刚提交的 GPU timing 可能被初始化清掉
另外的,std::scoped_lock 可以一次锁住多个 mutex,并使用标准库的死锁避免策略,比手写连续 lock() 更可以避免死锁的发生